///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef _Node_H__
#define _Node_H__

#include "OgrePrerequisites.h"

#include "OgreCommon.h"
#include "math/matrix3.h "
#include "math/matrix4.h "
#include "math/quaternion.h"
#include "OgreString.h"
#include "OgreRenderable.h"
#include "OgreIteratorWrappers.h"
#include "OgreMesh.h"
#include "OgreUserObjectBindings.h"

namespace Ogre {


    class _OgreExport Node : public MemAlloc_SceneCtrl
    {
    public:
        
        enum TransformSpace
        {
            /// Transform is relative to the local space
            TS_LOCAL,
            /// Transform is relative to the space of the parent node
            TS_PARENT,
            /// Transform is relative to world space
            TS_WORLD
        };
        typedef HashMap<String, Node*> ChildNodeMap;
        typedef MapIterator<ChildNodeMap> ChildNodeIterator;
		typedef ConstMapIterator<ChildNodeMap> ConstChildNodeIterator;

		
		class _OgreExport Listener
		{
		public:
			Listener() {}
			virtual ~Listener() {}
			
			virtual void nodeUpdated(const Node*) {}
			
			virtual void nodeDestroyed(const Node*) {}
			
			virtual void nodeAttached(const Node*) {}
			
			virtual void nodeDetached(const Node*) {}
		};

		
// 		class DebugRenderable : public Renderable, public MemAlloc_SceneCtrl
// 		{
// 		protected:
// 			Node* mParent;
// 			MeshPtr mMeshPtr;
// 			MaterialPtr mMat;
// 			Real mScaling;
// 		public:
// 			DebugRenderable(Node* parent);
// 			~DebugRenderable();
// 			const MaterialPtr& getMaterial(void) const;
// 			void getRenderOperation(RenderOperation& op);
// 			void getWorldTransforms(Matrix4* xform) const;
// 			Real getSquaredViewDepth(const Camera* cam) const;
// 			const LightList& getLights(void) const;
// 			void setScaling(Real s) { mScaling = s; }
// 
// 		};

    protected:
        /// Pointer to parent node
        Node* mParent;
        /// Collection of pointers to direct children; hashmap for efficiency
        ChildNodeMap mChildren;

		typedef set<Node*>::type ChildUpdateSet;
        /// List of children which need updating, used if self is not out of date but children are
        mutable ChildUpdateSet mChildrenToUpdate;
        /// Flag to indicate own transform from parent is out of date
        mutable bool mNeedParentUpdate;
		/// Flag indicating that all children need to be updated
		mutable bool mNeedChildUpdate;
		/// Flag indicating that parent has been notified about update request
	    mutable bool mParentNotified ;
        /// Flag indicating that the node has been queued for update
        mutable bool mQueuedForUpdate;

        /// Friendly name of this node, can be automatically generated if you don't care
        String mName;

        /// Incremented count for next name extension
        static NameGenerator msNameGenerator;

        /// Stores the orientation of the node relative to it's parent.
        Quaternion mOrientation;

        /// Stores the position/translation of the node relative to its parent.
        Vector3 mPosition;

        /// Stores the scaling factor applied to this node
        Vector3 mScale;

        /// Stores whether this node inherits orientation from it's parent
        bool mInheritOrientation;

        /// Stores whether this node inherits scale from it's parent
        bool mInheritScale;

        /// Only available internally - notification of parent.
        virtual void setParent(Node* parent);

        
        mutable Quaternion mDerivedOrientation;

        
        mutable Vector3 mDerivedPosition;

        
        mutable Vector3 mDerivedScale;

        
        virtual void _updateFromParent(void) const;

		
		virtual void updateFromParentImpl(void) const;


        
        virtual Node* createChildImpl(void) = 0;

        
        virtual Node* createChildImpl(const String& name) = 0;

        /// The position to use as a base for keyframe animation
        Vector3 mInitialPosition;
        /// The orientation to use as a base for keyframe animation
        Quaternion mInitialOrientation;
        /// The scale to use as a base for keyframe animation
        Vector3 mInitialScale;

        /// Cached derived transform as a 4x4 matrix
        mutable Matrix4 mCachedTransform;
        mutable bool mCachedTransformOutOfDate;

		
		Listener* mListener;

		typedef vector<Node*>::type QueuedUpdates;
		static QueuedUpdates msQueuedUpdates;

		//DebugRenderable* mDebug;

		/// User objects binding.
		UserObjectBindings mUserObjectBindings;

    public:
        
        Node();
        
        Node(const String& name);

        virtual ~Node();  

        
        const String& getName(void) const;

        
        virtual Node* getParent(void) const;

        
        virtual const Quaternion & getOrientation() const;

        
        virtual void setOrientation( const Quaternion& q );

        
        virtual void setOrientation( Real w, Real x, Real y, Real z);

        
        virtual void resetOrientation(void);

        
        virtual void setPosition(const Vector3& pos);

        
        virtual void setPosition(Real x, Real y, Real z);

        
        virtual const Vector3 & getPosition(void) const;

        
        virtual void setScale(const Vector3& scale);

        
        virtual void setScale(Real x, Real y, Real z);

        
        virtual const Vector3 & getScale(void) const;

        
        virtual void setInheritOrientation(bool inherit);

        
        virtual bool getInheritOrientation(void) const;

        
        virtual void setInheritScale(bool inherit);

        
        virtual bool getInheritScale(void) const;

        
        virtual void scale(const Vector3& scale);

        
        virtual void scale(Real x, Real y, Real z);

        
        virtual void translate(const Vector3& d, TransformSpace relativeTo = TS_PARENT);
        
        virtual void translate(Real x, Real y, Real z, TransformSpace relativeTo = TS_PARENT);
        
        virtual void translate(const Matrix3& axes, const Vector3& move, TransformSpace relativeTo = TS_PARENT);
        
        virtual void translate(const Matrix3& axes, Real x, Real y, Real z, TransformSpace relativeTo = TS_PARENT);

        
        virtual void roll(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

        
        virtual void pitch(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

        
        virtual void yaw(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

        
        virtual void rotate(const Vector3& axis, const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

        
        virtual void rotate(const Quaternion& q, TransformSpace relativeTo = TS_LOCAL);

        
        virtual Matrix3 getLocalAxes(void) const;

        
        virtual Node* createChild(
            const Vector3& translate = Vector3::ZERO, 
            const Quaternion& rotate = Quaternion::IDENTITY );

        
        virtual Node* createChild(const String& name, const Vector3& translate = Vector3::ZERO, const Quaternion& rotate = Quaternion::IDENTITY);

        
        virtual void addChild(Node* child);

        
        virtual unsigned short numChildren(void) const;

        
        virtual Node* getChild(unsigned short index) const;    

        
        virtual Node* getChild(const String& name) const;

        
        virtual ChildNodeIterator getChildIterator(void);

        
		virtual ConstChildNodeIterator getChildIterator(void) const;

        
        virtual Node* removeChild(unsigned short index);
        
        virtual Node* removeChild(Node* child);

        
        virtual Node* removeChild(const String& name);
        
        virtual void removeAllChildren(void);
		
		
		virtual void _setDerivedPosition(const Vector3& pos);

		
		virtual void _setDerivedOrientation(const Quaternion& q);

		
        virtual const Quaternion & _getDerivedOrientation(void) const;

        
        virtual const Vector3 & _getDerivedPosition(void) const;

        
        virtual const Vector3 & _getDerivedScale(void) const;

        
        virtual const Matrix4& _getFullTransform(void) const;

        
        virtual void _update(bool updateChildren, bool parentHasChanged);

        
		virtual void setListener(Listener* listener) { mListener = listener; }
		
		
		virtual Listener* getListener(void) const { return mListener; }
		

        
        virtual void setInitialState(void);

        
        virtual void resetToInitialState(void);

        
        virtual const Vector3& getInitialPosition(void) const;
		
		
		virtual Vector3 convertWorldToLocalPosition( const Vector3 &worldPos );

		
		virtual Vector3 convertLocalToWorldPosition( const Vector3 &localPos );

		
		virtual Quaternion convertWorldToLocalOrientation( const Quaternion &worldOrientation );

		
		virtual Quaternion convertLocalToWorldOrientation( const Quaternion &localOrientation );

		
        virtual const Quaternion& getInitialOrientation(void) const;

        
        virtual const Vector3& getInitialScale(void) const;

        
        virtual Real getSquaredViewDepth(const Camera* cam) const;

        
        virtual void needUpdate(bool forceParentUpdate = false);
        
        virtual void requestUpdate(Node* child, bool forceParentUpdate = false);
        
        virtual void cancelUpdate(Node* child);

//		virtual DebugRenderable* getDebugRenderable(Real scaling);

		static void queueNeedUpdate(Node* n);
		
		static void processQueuedUpdates(void);

		virtual void setUserAny(const Any& anything) { getUserObjectBindings().setUserAny(anything); }

		
		virtual const Any& getUserAny(void) const { return getUserObjectBindings().getUserAny(); }

		
		UserObjectBindings&	getUserObjectBindings() { return mUserObjectBindings; }

		
		const UserObjectBindings& getUserObjectBindings() const { return mUserObjectBindings; }

    };
	
	

} //namespace

#endif
